package org.scboy.common.dynamic;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

import org.scboy.common.annotation.ScriptNode;
import org.scboy.common.dynamic.cache.NodeCache;
import org.scboy.common.dynamic.edge.BaseEdge;
import org.scboy.common.dynamic.endpoint.BaseEndpoint;
import org.scboy.common.dynamic.node.BaseNode;
import org.scboy.coobi.sql.Coobi;
import org.scboy.coobi.sql.factory.EntityFactory;
import org.scboy.utils.entitys.coobi.Model;
import org.springframework.web.client.RestTemplate;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;

/**
 * 动态脚本
 */
public class DynamicScript {
	
	private StringBuilder messageBuffer=new StringBuilder();
	
	/**
	 * 数据表操作
	 */
	private EntityFactory factory;

	/**
	 * SQL操作
	 */
	private Coobi coobi;
	

	/**
	 * 服务调用
	 */
	private RestTemplate template;
	
	/**
	 * 是否测试
	 */
	private boolean test;
	
	
	public EntityFactory getFactory() {
		return factory;
	}

	public Coobi getCoobi() {
		return coobi;
	}


	public RestTemplate getTemplate() {
		return template;
	}

	public boolean isTest() {
		return test;
	}

	public void appendMsg(String msg) {
		messageBuffer.append("\r\n");
		messageBuffer.append(msg);
	}
	
	public String getTestMessage() {
		return messageBuffer.toString();
	}
	
	/**
	 * 节点缓存
	 */
	private Map<String, BaseNode> nodes= new HashMap<String, BaseNode>();
	
	/**
	 * endpoint缓存
	 */
	private Map<String, BaseEndpoint> endpoints= new HashMap<String, BaseEndpoint>();
	
	
	/**
	 * 开始节点
	 */
	public BaseNode startNode=null;
	
	/**
	 * 根据配置文件初始化
	 * @param designContext
	 */
	public  DynamicScript(String designContext,EntityFactory factory,Coobi coobi,RestTemplate template,boolean test) {
		
		this.factory=factory;
		
		this.coobi=coobi;
		
		this.template=template;
		
		this.test=test;
		
		JSONObject object=new JSONObject(designContext);
		
		/**
		 * 处理节点
		 */
		JSONArray _nodes = object.getJSONArray("nodes");
		for (Object _obj : _nodes) {
			JSONObject node=(JSONObject)_obj;
			Class<BaseNode> nodeClass = NodeCache.getNodeClass(node.getStr("nodeType"));
			if(nodeClass==null) {
				throw new RuntimeException("找不到动态节点实现类："+node.getStr("Class")+"，标记："+node.getStr("nodeType"));
			}
			try {
				//构建节点
				BaseNode nodeObject = nodeClass.getDeclaredConstructor().newInstance();
				nodeObject.create(node,this);
				nodeObject.setId(node.getStr("id"));
				nodeObject.setNodeName(node.getStr("text"));
				
				/**
				 * 处理连接点
				 */
				JSONArray _endpoints=node.getJSONArray("endpoints");
				for (Object _endpoint : _endpoints) {
					JSONObject endpoint=(JSONObject)_endpoint;
					
					//创建endpoint
					BaseEndpoint endpointObj=new BaseEndpoint();
					endpointObj.setId(endpoint.getStr("id"));
					endpointObj.setDataKey(endpoint.getStr("dataKey"));
					endpointObj.setName(endpoint.getStr("title"));
					String pointType=endpoint.getStr("pointType");
					switch (pointType) {
					case "start": {
						if(StrUtil.isEmpty(endpointObj.getName())) {
							endpointObj.setName("流入");
						}
						nodeObject.setRunForm(endpointObj);
						break;
					}
					case "end": {
						if(StrUtil.isEmpty(endpointObj.getName())) {
							endpointObj.setName("流出");
						}
						nodeObject.setRunTo(endpointObj);
						break;
					}
					case "input": {
						nodeObject.setInputEndpoint(endpointObj);
						break;
					}
					case "output": {
						nodeObject.setOutPutEndpoint(endpointObj);
						break;
					}
					default:
						throw new IllegalArgumentException("Unexpected value: " + pointType);
					}
					
					endpointObj.setNode(nodeObject);
					endpoints.put(endpointObj.getId(), endpointObj);
				}
				
				ScriptNode scriptNode=nodeClass.getAnnotation(ScriptNode.class);
				if(scriptNode.start()) {
					if(startNode!=null) {
						throw new RuntimeException("入口节点只能有一个，现在出现了两个入口节点："+startNode.getNodeName()+"，"+nodeObject.getNodeName());
					}else {
						startNode=nodeObject;
					}
				}
				
				//数据类型节点，直接先执行。
				if(scriptNode.isData()) {
					nodeObject.run();
				}
				
				nodes.put(nodeObject.getId(), nodeObject);
			} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
					| InvocationTargetException | NoSuchMethodException | SecurityException e) {
				throw new RuntimeException(e);
			}
		}

		/**
		 * 链路处理
		 */
		JSONArray _edges = object.getJSONArray("edges");
		for (Object _edge : _edges) {
			JSONObject edge=(JSONObject)_edge;
			BaseEdge edgeObj=new BaseEdge();
			String targetEndpointId=edge.getStr("targetEndpoint");
			String sourceEndpointId=edge.getStr("sourceEndpoint");
			BaseEndpoint targetEndpoint = endpoints.get(targetEndpointId);
			edgeObj.setTargetEndpoint(targetEndpoint);
			edgeObj.setTargetNode(targetEndpoint.getNode());

			BaseEndpoint sourceEndpoint = endpoints.get(sourceEndpointId);
			edgeObj.setSourceEndpoint(sourceEndpoint);
			edgeObj.setSourceNode(sourceEndpoint.getNode());
			targetEndpoint.setInputEdge(edgeObj);
			sourceEndpoint.setOutputEdge(edgeObj);
		}
		
		
	}
	
	public Object run(Model params) {
		
		//设置启动节点入参
		startNode.setData("params", params);
		/**
		 * 开始执行脚本
		 */
		if(startNode==null) {
			throw new RuntimeException("脚本没有开始节点，无法执行。");
		}else {
			return startNode.run();
		}
	}
	
	/**
	 * 创建动态脚本
	 * @param designContext
	 * @return
	 */
	public static DynamicScript create(String designContext,EntityFactory factory,Coobi coobi,RestTemplate template,boolean test) {
		DynamicScript script=new DynamicScript(designContext,factory,coobi,template,test);
		return script;
	}
}
